﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ClientManager.Data.Infrastucture
{
    public class GenericRepository<T> : IRepository<T> where T : class
    {
        protected DbContext _dbContext;
        protected IDbSet<T> _dbSet;

        public GenericRepository(DbContext dbContext)
        {
            _dbContext = dbContext;
            _dbSet = _dbContext.Set<T>();
        }

        public virtual void Add(T entity)
        {
            _dbContext.Entry(entity).State = EntityState.Added;
        }

        public virtual void Update(T entity)
        {
            _dbContext.Entry(entity).State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            _dbContext.Entry(entity).State = EntityState.Deleted;
        }

        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = _dbSet.Where(where).AsEnumerable();
            foreach (var obj in objects)
            {
                _dbSet.Remove(obj);
            }
        }

        public T GetById(int id)
        {
            return _dbSet.Find(id);
        }


        public T Get(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includes)
        {
            return GetQuery(null, includes).SingleOrDefault(where);
        }

        public IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includes)
        {
            return GetQuery(null, includes).ToList();
        }

        public IEnumerable<T> GetMany(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includes)
        {
            return GetQuery(where, includes).ToList();
        }

     
        /// <summary>
        /// Applies filters, order and includes to a query and returns it.
        /// </summary>
        /// <param name="where"></param>
        /// <param name="orderBy"></param>
        /// <param name="includes"></param>
        protected virtual IQueryable<T> GetQuery<TOrder>(
            Expression<Func<T, bool>> where = null,
            Expression<Func<T, TOrder>> orderBy = null,
            params Expression<Func<T, object>>[] includes)
        {
            IQueryable<T> query = _dbSet;

            if (where != null)
                query = query.Where(where);

            if (includes != null)
                query = includes.Aggregate(query, (current, include) => current.Include(include));

            if (orderBy != null)
                query = query.OrderBy(orderBy);

            return query;
        }
        /// <summary>
        /// Applies filters, order and includes to a query and returns it.
        /// </summary>
        /// <param name="where"></param>
        /// <param name="includes"></param>
        protected virtual IQueryable<T> GetQuery(
            Expression<Func<T, bool>> where = null,
            params Expression<Func<T, object>>[] includes)
        {
            IQueryable<T> query = _dbSet;

            if (where != null)
                query = query.Where(where);

            if (includes != null)
                query = includes.Aggregate(query, (current, include) => current.Include(include));

            return query;
        }
    }
}
